#!/usr/bin/python
# 百度POI爬取
# https://gitee.com/codeadas/crawling-baidu-poi
# -*- coding: UTF-8 -*-
import requests
import xlwt
import os
from tqdm import tqdm
import time
import math
import json

url = "http://api.map.baidu.com/place/v2/search"

params = {}
params["query"] = "美食"  # 初始类型
# 按照行政或者矩形 行政则一共400条，为突破访问限制，优先选用矩形搜索
# params["region"] = "南昌"  # 行政搜索
params["bounds"] = "28.16,115.43,29.14,116.57"  # 矩形初始位置搜索，28.16,115.43,29.14,116.57 lat,lng(左下角坐标),lat,lng(右上角坐标)
params["page_size"] = 20  # 单次召回POI数量，默认为10条记录，最大返回20条。多关键字检索时，返回的记录数为关键字个数*page_size。
params["page_num"] = 0  # 分页页码，默认为0,0代表第一页，1代表第二页，以此类推。
params["output"] = "json"  # 返回类型
params["coord_type"] = 1  # 坐标类型wgs84
params["ak"] = "****************"
params["timestamp"] = str(int(time.time()))  # 时间戳
params["sn"] = "****************"  # 权限签名
# L = requests.get(url=url, params=params).json()  # 测试用


# 保存文件
class ExcelHelper(object):
    @staticmethod
    def create_excel(column_heads, row_items, file_path):
        book = xlwt.Workbook(encoding='utf-8', style_compression=0)
        sheet = book.add_sheet('sheet1', cell_overwrite_ok=True)
        column_count = len(column_heads)
        row_count = len(row_items)
        # 写入列标题
        for column_id in range(column_count):
            sheet.write(0, column_id, column_heads[column_id])
        # 遍历所有行
        for row_id in range(row_count):
            row_item = row_items[row_id]
            # 遍历每行的所有列
            for column_id in range(column_count):
                # 将每一行的对应列写sheet中
                sheet.write(row_id + 1, column_id, row_item[column_id])
        # 保存文件
        if os.path.exists(file_path):
            os.remove(file_path)
        book.save(file_path)


# 保存文件
def Save_table_data():
    # 保存数据
    # 新版：0：中性； 1：褒义； -1：贬义
    column_heads = ["POI类型", "地址", "城市名", "区名", "lat", "lng"]
    row_items = Save_table
    time_now = time.time()
    t = time.strftime('%Y-%m-%d-%H-%M-%S ', time.localtime(time_now))
    file_path = "./百度POI爬取结果_%s.xls" % t
    ExcelHelper.create_excel(
        column_heads=column_heads,
        row_items=row_items,
        file_path=file_path
    )
    print("爬取完成，结果保存于： {0}".format(file_path))


def splitArea(bound, row_num):
    # 分割矩形区域的函数
    boundList = []
    # row_num = 50  # 按照 50 X 50 进行分割
    bound = list(map(float, bound.split(',')))
    step_lat = (bound[2] - bound[0]) / row_num
    step_lon = (bound[3] - bound[1]) / row_num
    for i in range(0, row_num):
        for j in range(0, row_num):
            boundTemp = []
            boundTemp.append(bound[0] + step_lat * i)
            boundTemp.append(bound[1] + step_lon * j)
            boundTemp.append(bound[0] + step_lat * (i + 1))
            boundTemp.append(bound[1] + step_lon * (j + 1))
            boundTemp = ",".join(["%s" % x for x in boundTemp])
            boundList.append(boundTemp)
    return boundList


def main(Save_table):
    # 开始循环处理
    # POI类型选择
    # for i in tqdm(range(len(POI_Size))):

    for BoundList_i in tqdm(range(len(BoundList))):
        # print("第" + str(BoundList_i) + "个网格")  # 查看BUG进度

        # 当前网格块
        params["bounds"] = BoundList[BoundList_i]

        page = 0  # 初始页码
        params["page_num"] = page  # 更新页码

        # 发送GET请求
        params["timestamp"] = str(int(time.time()))  # 时间戳更新
        try:
            req = requests.get(url=url, params=params)
            time.sleep(0.05)  # 防止高并发，休息0.05秒

            # 对返回数据进行json处理
            L = json.loads(req.text, strict=False)  # 防止json格式不对
        except Exception as re:
            print(re)
            return Save_table

        # 解析数据
        if L["status"] != 0:  # 请求是否成功
            print("API访问请求不成功,状态码为status: " + str(L["status"]))
            # 不再继续访问,将数据传回,或根据状态码做进一步开发
            if L["status"] == 4:
                print("服务当日调用次数已用完!! 当前请求POI类型为: " + str(params["query"]) + ",遍历网格数为: " + str(BoundList_i))
                # 不再继续访问,将数据传回,或根据状态码做进一步开发
                return Save_table
            return Save_table

        else:
            # print(" 请求成功")
            req_total = L['total']  # 返回数据总数
            req_results = L["results"]  # 返回数据

            req_len = len(req_results)  # 此次返回数据个数
            # 对数据进行解析,整理存取
            List_table = List_table_save(req_results, req_len)
            Save_table += List_table  # 将数据添加到保存列表

            # 如果返回数据大于20条,则进行翻页操作,重新发送请求
            Page_total = math.ceil(req_total / 20) - 1  # 总共有多少页,0页开始,故减1
            while page <= Page_total:
                page += 1  # 页数增加
                params["page_num"] = page  # 页码更新

                # 重新发送GET请求
                params["timestamp"] = str(int(time.time()))  # 时间戳更新
                try:
                    req = requests.get(url=url, params=params)
                    time.sleep(0.05)  # 防止高并发，休息0.1秒

                    # 对返回数据进行json处理
                    L = json.loads(req.text, strict=False)  # 防止json格式不对
                    # L = req.json()  # requests 自带json
                    print("第" + str(page) + "页,共有" + str(L['total']) + "个数据")
                except Exception as re:
                    print(re)
                    return Save_table

                if L["status"] != 0:  # 请求是否成功
                    print("API访问请求不成功,状态码为status: " + str(L["status"]))
                    # 不再继续访问,将数据传回,或根据状态码做进一步开发
                    return Save_table
                else:
                    # print(" 请求成功")
                    req_results = L["results"]  # 返回数据

                    req_len = len(req_results)  # 此次返回数据个数
                    # 对数据进行解析,整理存取
                    List_table = List_table_save(req_results, req_len)
                    Save_table += List_table  # 将数据添加到保存列表
    return Save_table


def List_table_save(req_results, req_len):
    # 将临时子列表添加入到保存列表中
    _Table_all = []
    for n in range(req_len):
        _table = [params["query"]]  # 临时储存列表,加入此次POI访问类型
        try:
            range_n = req_results[n]  # 单个地区返回具体文本
            range_name = range_n["name"]  # POI详细名字
            range_city = range_n["city"]  # 城市名字
            range_area = range_n["area"]  # 区县名
            range_location = range_n["location"]
            range_location_lat = range_location["lat"]
            range_location_lng = range_location['lng']

            # 临时列表添加
            _table.append(range_name)
            _table.append(range_city)
            _table.append(range_area)
            _table.append(range_location_lat)
            _table.append(range_location_lng)

            _Table_all.append(_table)  # 将临时子列表加入到临时总列表中
        except Exception as re:
            print(re)
            return [params["query"], '出错', '出错', '出错', 0, 0]
    return _Table_all


if __name__ == '__main__':
    # 兴趣点类型 22种
    POI_Size = ["美食", "酒店", "购物", "生活服务", "丽人", "旅游景点", "休闲娱乐", "运动健身", "教育培训", "文化传媒", "医疗", "汽车服务", "交通设施", "金融",
                "房地产", "公司企业", "政府机构", "出入口", "自然地物", "行政地标", "门址"]

    Bound = "28.16,115.43,29.14,116.57"  # 自定义矩形范围,左下加右上
    row_num = 50  # 对Bound矩阵,按照 50 X 50 进行分割
    BoundList = splitArea(Bound, row_num)  # 将矩形按照方框格式进行切割

    # 继续上次遍历(断联则用)
    # BL_continue = 1544  # 此处为上次断联地图序号
    # BoundList = BoundList[BL_continue:]

    # params["query"] = POI_Size[6]
    # params["query"] = "行政地标"
    # Save_table = []  # 最终储存列表初始化
    # # 开始运行
    # Save_table = main(Save_table)
    # Save_table_data()  # 保存

    for POI_i in range(len(POI_Size)):
        params["query"] = POI_T[POI_i]  # POI类型文本
        Save_table = []  # 最终储存列表初始化
        # 开始运行
        Save_table = main(Save_table)
        Save_table_data()  # 保存
